COMP3151/9154 Foundations of Concurrency
Term 2, 2022

Wednesday Code and Notes (Week 5)

Table of Contents

1 Lecture Notes

Synchronous channels:

  - send and receive must happen together

Asynchronous channels:

  A special case of asynch channels:
    reliable FIFO channels

  - send is non-blocking; puts a message in a queue
  - receive pops a message from a queue
    receive blocks if there aren't messages

Q: what happens if the queue is full:
A: varies from channel to channel
   It might:
   - block until there's space
       (that's what we implemented in Producer-Consumer)
   - drop messages if there's no space


Conway's problem example:

K = 5
Let's say the input is:

  aabaaaaabbaaaaaaaaaaaaaaaaabc .....

The output should be:
  2ab5a\n2b9a7\nabc ...


In a synchronous transition diagram,
 instead of having transitions

   l,l': starting location; destination location
   b: guard
   f: state update

   l -- b; f ---> l'

Now we'll have three kinds of transitions:

*internal transitions*

   l -- b; f ---> l'

*send transition*

   send x on ch; and also guard b; state update f

   l -- b; ch ⇐ x;f ---> l'

*receive transition*

   send x on ch; and also guard b; state update f

   l -- b; ch ⇒ x;f ---> l'

2 Promela Examples

2.1 Channel basics

Synchronous hello world:

/* a channel called ch
     with queue capacity 0 (synchronous)
     can transmit messages of type byte
 */ 
chan ch = [0] of { byte }

active proctype P() {
  ch ! 30; // send 30 on channel ch
  ch ! 55;
}

active proctype Q() {
  byte x=0;
  ch ? x; // receive a message on ch; save to xmi
  printf("I received: %d\n",x);
  ch ? x;
  printf("I received: %d\n",x);  
}

Asynchronous hello world:

/* a channel called ch
     with queue capacity 1 (asynchronous)
     can transmit messages of type byte
 */ 
chan ch = [1] of { byte }

active proctype P() {
  ch ! 30; // send 30 on channel ch
  ch ! 55;

  /* Two kinds of send:
       ch ! x  // put a message at the end of the queue
       ch !! x // "sort" a message into the queue     
     Four kinds of receive:
       ch ? x    // pop the first message from the queue
                    if it matches our pattern
       ch ? <x>  // read the first message from the queue
                    *without* removing it from the queue
       ch ?? x    // pop the first message from the queue
                     *that* matches our pattern
       ch ?? <x>  // read the first message from the queue
                     *that* matches our pattern
   */
}

active proctype Q() {
  byte x=0;
  ch ? x; // receive a message on ch; save to x
  printf("I received: %d\n",x);
  ch ? x;
  printf("I received: %d\n",x);  
}

Blocking vs non-blocking send:

chan ch = [0] of { byte }
chan ach = [5] of { byte }

active proctype P() {
  ch ! 30; // this send never happens; nobody is receiving and the channel is synchronous

  printf("** P sent a message!\n") // also never happens
}

active proctype R() {
  ach ! 30; // this send does happen; it goes in the queue

  printf("** R sent a message!\n")
}

2.2 Arithmetic server

/*
   b,x,y,ch

   b is true if you want to add; false if you want to subtract
   x y: the numbers you want to operate on
   ch: the channel you expect your reply on
 */
chan public = [0] of { bool, int, int, chan }

active proctype server() {
  int x;
  int y;
  chan session;
  do
       /* receive where we only accept messages where the bool component is 1 */
    :: public?1,x,y,session ->
       session!x+y
    :: public?0,x,y,session ->
       session!x-y
  od
  /* Q: will it block or go to the next branch?
     A: the do blocks until at least one branch is non-blocking
        when that happens, one of the non-blocking branches is chosen non-deterministically
   */
}

active proctype add_client() {
  chan add_channel = [0] of { int};
  int x = 0;
  public!1,1,1,add_channel;
  add_channel?x;
  printf("** 1 + 1 is %d, according to the server\n",x)
}

active proctype subtract_client() {
  chan subtract_channel = [0] of { int };
  int x = 0;
  public!0,1,1,subtract_channel;
  subtract_channel?x;
  printf("** 1 - 1 is %d, according to the server\n",x)
}

2.3 Conway's Problem

#define MAX 9
#define K 4

chan inC = [0] of { byte };
chan outC = [0] of { byte };
chan pipe = [0] of { byte };

proctype compress() {
  byte c = 0;
  byte previous = 0;
  byte n = 0;
  inC ? previous;
  do
  :: inC ? c;
     if
     :: c == previous && n < MAX - 1 ->
        n = n + 1;
     :: else ->
        if
        :: n > 0 ->
           pipe ! (n + 49);
           n = 0;
        :: else -> skip;
        fi;
        pipe ! previous;
        previous = c;
     fi;
  od;
}

proctype output() {
  byte c = 0;
  byte m = 0;
  do
  :: pipe ? c;
     outC ! c;
     m = m + 1;
     if
     :: m >= K ->
        outC ! 10;
        m = 0;
     :: else -> skip
     fi;
  od;
}

/* The sink just prints any characters it receives to stdout */
proctype sink() {
  byte c = 0;
  do
  :: outC ? c ->
     printf("%c",c);
  od;
}

/* Transmit the character sequence AAAAABC over and over */
proctype source() {
  do
  :: inC ! 65;
     inC ! 65;
     inC ! 65;
     inC ! 65;
     inC ! 65;     
     inC ! 66;
     inC ! 67;
  od;
}

init {
  run compress();
  run output();

  run source();
  run sink();
}

2.4 Multiplier (from Matrix transputer)

This is not a stand-alone model.

proctype Multiplier(byte Coeff;
                    chan North;
                    chan East;
                    chan South;
                    chan West)
{
  byte Sum, X;
  for (i : 0..(SIZE-1)) {
    if :: North ? X -> East ? Sum;
       :: East  ? Sum -> North ? X;
    fi;
    South ! X;
    Sum = Sum + X*Coeff;
    West  ! Sum;
  }
}

2.5 Dining philosophers

Note that this solution may deadlock, for the same reason as the lock-based solution we considered earlier.

You may check this by using Promela to search for invalid end-states.

You can apply the same fix as before to remedy this (imposing a total order on the forks to break the symmetry).

#define N 3

chan forks[N] = [0] of { bit };

inline think() {
  do
  :: true -> skip;
  :: true -> break;
  od
}

proctype philosopher(byte i) {
  do
  :: think();
     // pattern matching
     // receive the first message in the queue
     // but only if it equals true
     forks[i] ? true;
     forks[(i + 1) % N] ? true;
     // eat
     forks[i] ! true;
     forks[(i + 1) % N] ! true;
  od
}

proctype fork(byte i) {
  do
  :: forks[i] ! true;
     forks[i] ? true;
  od
}

init {
  byte i = 1;
  for(i : 1 .. N) {
    run philosopher(i-1);
    run fork(i-1);    
  }
}

2022-08-05 Fri 16:47

Announcements RSS